﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Runtime.Serialization;
using System.IO.IsolatedStorage;
using System.IO;

namespace Push.Entities
{
    [DataContractAttribute]
    public class DiscEntity
    {
        private Disc disc_;
        public Disc disc
        {
            get { return disc_; }
            set
            {
                disc_ = value;
                width = disc.Width;

                Initialize();
            }
        }

        public const int MinimumSize = 20;
        public const int MaximumSize = 40;
        
        public const double Friction = 90;
        public const double AngularFriction = 1.0;
        // how angular movements affects disc direction. Needs to be updated
        public const double Curl = 10.0;

        [DataMemberAttribute]
        public double DistanceToTarget = double.MaxValue;

        [DataMemberAttribute]
        public DiscColor discColor { get; set; }

        [DataMemberAttribute]
        public int ID { get; set; }

        private double width;
        private double width_2;
        [DataMemberAttribute]
        public double Radius
        {
            get
            {
                return width_2;
            }
            set
            {
                width_2 = value;
                width = width_2 * 2;
            }
        }

        [DataMemberAttribute]
        public double x;
        public double X
        {
            get
            {
                return GameManager.ConvertXBack(x);
            }
            set
            {
                x = GameManager.ConvertX(value);
                disc.Margin = new Thickness(x - width_2, y - width_2, 0, 0);
            }
        }

        [DataMemberAttribute]
        public double y;
        public double Y
        {
            get
            {
                return GameManager.ConvertYBack(y);
            }
            set
            {
                y = GameManager.ConvertY(value);
                disc.Margin = new Thickness(x - width_2, y - width_2, 0, 0);
            }
        }

        [DataMemberAttribute]
        public double rotation;
        public double Rotation
        {
            get
            {
                return rotation;
            }
            set
            {
                rotation = value;
                disc.disc.RenderTransform = new CompositeTransform() { Rotation = rotation, CenterX = disc.disc.Width / 2, CenterY = disc.disc.Height / 2 };
            }
        }

        [DataMemberAttribute]
        public double VelocityX { get; set; }
        [DataMemberAttribute]
        public double VelocityY { get; set; }
        [DataMemberAttribute]
        public double AngularVelocity { get; set; }
        [DataMemberAttribute]
        public double Mass { get; set; }

        [DataMemberAttribute]
        public bool IsStatic = true;

        [DataMemberAttribute]
        public DiscState State { get; set; }

        public DiscEntity()
        {
            Mass = 1.0;
            State = DiscState.InGame;
        }

        public DiscEntity(Disc disc) : this()
        {
            this.disc = disc;
        }

        public void Initialize()
        {
            //X = X;
            //Y = Y;
            //Rotation = Rotation;
            Mass = 1.0;
            //State = State;
        }

        public void Save()
        {
            //if (!IsStatic)
            // {
            using (IsolatedStorageFile Store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!Store.DirectoryExists("Discs"))
                {
                    Store.CreateDirectory("Discs");
                }

                string Path = "\\Discs\\" + ID + ".xml";

                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(Path, FileMode.Create, Store))
                {
                    DataContractSerializer dtaContractSerializer = new DataContractSerializer(typeof(DiscEntity));
                    dtaContractSerializer.WriteObject(stream, this);
                }
            }
            //}
        }

        public static DiscEntity Load(int ID)
        {
            using (IsolatedStorageFile Store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!Store.DirectoryExists("Discs"))
                {
                    Store.CreateDirectory("Discs");
                }

                string Path = "\\Discs\\" + ID + ".xml";

                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(Path, FileMode.OpenOrCreate, Store))
                {
                    if (stream.Length > 0)
                    {
                        DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(DiscEntity));
                        return dataContractSerializer.ReadObject(stream) as DiscEntity;
                    }
                    return null;
                }
            }
        }

        public void Update(double dT)
        {
            UpdatePosition(VelocityX, VelocityY, AngularVelocity, dT);
            UpdateVelocity(Friction, AngularFriction, dT);
        }

        private void UpdatePosition(double VelocityX, double VelocityY, double AngularVelocity, double dT)
        {
            if (Math.Abs(VelocityX) > 0.0 || Math.Abs(VelocityY) > 0.0/* || Math.Abs(AngularVelocity) > 0.0*/)
            {
                X += VelocityX * dT;
                Y += VelocityY * dT;
                /*Rotation += AngularVelocity * dT;*/

                IsStatic = false;
            }
            else
            {
                if (!IsStatic)
                {
                    //DistanceToTarget = Math.Sqrt(Math.Pow(X - GameManager.Centerline, 2) + Math.Pow(Y - (GameManager.RinkLength - GameManager.Teeline), 2));
                    IsStatic = true;
                }
            }
        }

        private void UpdateVelocity(double Friction, double AngularFriction, double dT)
        {
            if (!IsStatic)
            {
                double Velocity = 0.0;

                if (Math.Abs(VelocityX) > 0.0 || Math.Abs(VelocityY) > 0.0)
                {
                    Velocity = Math.Sqrt(VelocityX * VelocityX + VelocityY * VelocityY);

                    //double Va = AngularVelocity * 0.0000005 * Curl * (1 / (Velocity / 20 + 1) + 1);

                    //double Vx = VelocityX * Math.Cos(-Va) - VelocityY * Math.Sin(-Va);
                    //double Vy = VelocityY * Math.Cos(Va) + VelocityX * Math.Sin(Va);

                    //VelocityX = Vx;
                    //VelocityY = Vy;

                    //PlayMoveSound(Velocity);

                    double NewVelocity = Math.Max(Velocity - Friction * dT, 0.0);

                    double d = NewVelocity / Velocity;

                    VelocityX *= d;
                    VelocityY *= d;
                }

                double NewAngularVelocity = AngularVelocity - Math.Sign(AngularVelocity) * AngularFriction * Math.Min((20 / Velocity + 1), 40) * dT;

                AngularVelocity = (Math.Sign(AngularVelocity) == Math.Sign(NewAngularVelocity)) ? NewAngularVelocity : 0.0;
            }
        }

        public void CheckPosition()
        {
            if (!IsStatic)
            {
                if (X - Radius <= 0 || X + Radius >= GameManager.TotalWidth)
                {
                    VelocityX *= -1;

                    GameManager.vibrate.Start(TimeSpan.FromMilliseconds(Math.Max(Math.Min(VelocityX / 5, 50), 20)));
                }

                if (Y - Radius <= 0 || Y + Radius >= GameManager.TotalHeight)
                {
                    VelocityY *= -1;

                    GameManager.vibrate.Start(TimeSpan.FromMilliseconds(Math.Max(Math.Min(VelocityY / 5, 50), 20)));
                }
            }
        }

        public double CheckCollision(DiscEntity disc)
        {
            //if (State != StoneState.InOut && stone.State != StoneState.InOut)
            //{
            if (X + Radius >= disc.X - Radius && X - Radius <= disc.X + Radius && Y + Radius >= disc.Y - Radius && Y - Radius <= disc.Y + Radius)
                {
                    double DistanceSquare = (X - disc.X) * (X - disc.X) + (Y - disc.Y) * (Y - disc.Y);

                    if (DistanceSquare <= 4 * Radius * Radius)
                    {
                        //IsProceed = false;
                        IsStatic = false;
                        //stone.IsProceed = false;
                        disc.IsStatic = false;
                        return ProcessCollision(this, disc);
                    }
                }
            //}

            return 0;
        }

        private static double ProcessCollision(DiscEntity disc1, DiscEntity disc2)
        {
            double x_axis_X = disc2.X - disc1.X;
            double x_axis_Y = disc2.Y - disc1.Y;
            double len = Math.Sqrt(x_axis_X * x_axis_X + x_axis_Y * x_axis_Y);
            x_axis_X /= len;
            x_axis_Y /= len;
            // CHECK FOR BUGS IN MASS
            double p = (x_axis_X * disc2.VelocityX + x_axis_Y * disc2.VelocityY) * disc2.Mass - (x_axis_X * disc1.VelocityX + x_axis_Y * disc1.VelocityY) * disc1.Mass;
            if (p < 0)
            {
                double dpx = x_axis_X * p;
                double dpy = x_axis_Y * p;
                disc1.VelocityX += dpx / disc1.Mass;
                disc1.VelocityY += dpy / disc1.Mass;

                disc2.VelocityX -= dpx / disc2.Mass;
                disc2.VelocityY -= dpy / disc2.Mass;

                disc1.AngularVelocity -= 0.5 * p * (x_axis_Y - x_axis_X);
                disc2.AngularVelocity += 0.5 * p * (x_axis_Y - x_axis_X);

                return -p;
            }

            return 0;
        }

        public void AddRotationImpulse(double RotationImpulse, double dT)
        {
            AngularVelocity += RotationImpulse * dT;
            AngularVelocity = Math.Max(Math.Min(AngularVelocity, 200), -200);
        }
    }
}
